153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2009 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as
853a5a1b3Sopenharmony_ci  published by the Free Software Foundation; either version 2.1 of the
953a5a1b3Sopenharmony_ci  License, or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  Lesser General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public
1753a5a1b3Sopenharmony_ci  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <fcntl.h>
2553a5a1b3Sopenharmony_ci#include <unistd.h>
2653a5a1b3Sopenharmony_ci#include <errno.h>
2753a5a1b3Sopenharmony_ci
2853a5a1b3Sopenharmony_ci/* Some versions of tdb lack inclusion of signal.h in the header files but use sigatomic_t */
2953a5a1b3Sopenharmony_ci#include <signal.h>
3053a5a1b3Sopenharmony_ci#include <tdb.h>
3153a5a1b3Sopenharmony_ci
3253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3353a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3553a5a1b3Sopenharmony_ci
3653a5a1b3Sopenharmony_ci#include "database.h"
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#define MAKE_TDB_CONTEXT(x) ((struct tdb_context*) (x))
3953a5a1b3Sopenharmony_ci
4053a5a1b3Sopenharmony_cistatic inline TDB_DATA* datum_to_tdb(TDB_DATA *to, const pa_datum *from) {
4153a5a1b3Sopenharmony_ci    pa_assert(from);
4253a5a1b3Sopenharmony_ci    pa_assert(to);
4353a5a1b3Sopenharmony_ci
4453a5a1b3Sopenharmony_ci    to->dptr = from->data;
4553a5a1b3Sopenharmony_ci    to->dsize = from->size;
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_ci    return to;
4853a5a1b3Sopenharmony_ci}
4953a5a1b3Sopenharmony_ci
5053a5a1b3Sopenharmony_cistatic inline pa_datum* datum_from_tdb(pa_datum *to, const TDB_DATA *from) {
5153a5a1b3Sopenharmony_ci    pa_assert(from);
5253a5a1b3Sopenharmony_ci    pa_assert(to);
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci    to->data = from->dptr;
5553a5a1b3Sopenharmony_ci    to->size = from->dsize;
5653a5a1b3Sopenharmony_ci
5753a5a1b3Sopenharmony_ci    return to;
5853a5a1b3Sopenharmony_ci}
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_civoid pa_datum_free(pa_datum *d) {
6153a5a1b3Sopenharmony_ci    pa_assert(d);
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_ci    free(d->data); /* tdb uses raw malloc/free hence we should do that here, too */
6453a5a1b3Sopenharmony_ci    pa_zero(d);
6553a5a1b3Sopenharmony_ci}
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_cistatic struct tdb_context *tdb_open_cloexec(
6853a5a1b3Sopenharmony_ci        const char *name,
6953a5a1b3Sopenharmony_ci        int hash_size,
7053a5a1b3Sopenharmony_ci        int tdb_flags,
7153a5a1b3Sopenharmony_ci        int open_flags,
7253a5a1b3Sopenharmony_ci        mode_t mode) {
7353a5a1b3Sopenharmony_ci
7453a5a1b3Sopenharmony_ci    /* Mimics pa_open_cloexec() */
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci    struct tdb_context *c;
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci#ifdef O_NOCTTY
7953a5a1b3Sopenharmony_ci    open_flags |= O_NOCTTY;
8053a5a1b3Sopenharmony_ci#endif
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_ci#ifdef O_CLOEXEC
8353a5a1b3Sopenharmony_ci    errno = 0;
8453a5a1b3Sopenharmony_ci    if ((c = tdb_open(name, hash_size, tdb_flags, open_flags | O_CLOEXEC, mode)))
8553a5a1b3Sopenharmony_ci        goto finish;
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_ci    if (errno != EINVAL)
8853a5a1b3Sopenharmony_ci        return NULL;
8953a5a1b3Sopenharmony_ci#endif
9053a5a1b3Sopenharmony_ci
9153a5a1b3Sopenharmony_ci    errno = 0;
9253a5a1b3Sopenharmony_ci    if (!(c = tdb_open(name, hash_size, tdb_flags, open_flags, mode)))
9353a5a1b3Sopenharmony_ci        return NULL;
9453a5a1b3Sopenharmony_ci
9553a5a1b3Sopenharmony_cifinish:
9653a5a1b3Sopenharmony_ci    pa_make_fd_cloexec(tdb_fd(c));
9753a5a1b3Sopenharmony_ci    return c;
9853a5a1b3Sopenharmony_ci}
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ciconst char* pa_database_get_filename_suffix(void) {
10153a5a1b3Sopenharmony_ci    return ".tdb";
10253a5a1b3Sopenharmony_ci}
10353a5a1b3Sopenharmony_ci
10453a5a1b3Sopenharmony_cipa_database* pa_database_open_internal(const char *path, bool for_write) {
10553a5a1b3Sopenharmony_ci    struct tdb_context *c;
10653a5a1b3Sopenharmony_ci
10753a5a1b3Sopenharmony_ci    pa_assert(path);
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci    if ((c = tdb_open_cloexec(path, 0, TDB_NOSYNC|TDB_NOLOCK, (for_write ? O_RDWR|O_CREAT : O_RDONLY), 0644)))
11053a5a1b3Sopenharmony_ci        pa_log_debug("Opened TDB database '%s'", path);
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    if (!c) {
11353a5a1b3Sopenharmony_ci        if (errno == 0)
11453a5a1b3Sopenharmony_ci            errno = EIO;
11553a5a1b3Sopenharmony_ci        return NULL;
11653a5a1b3Sopenharmony_ci    }
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci    return (pa_database*) c;
11953a5a1b3Sopenharmony_ci}
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_civoid pa_database_close(pa_database *db) {
12253a5a1b3Sopenharmony_ci    pa_assert(db);
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    tdb_close(MAKE_TDB_CONTEXT(db));
12553a5a1b3Sopenharmony_ci}
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_cipa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) {
12853a5a1b3Sopenharmony_ci    TDB_DATA tdb_key, tdb_data;
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    pa_assert(db);
13153a5a1b3Sopenharmony_ci    pa_assert(key);
13253a5a1b3Sopenharmony_ci    pa_assert(data);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci    return tdb_data.dptr ?
13753a5a1b3Sopenharmony_ci        datum_from_tdb(data, &tdb_data) :
13853a5a1b3Sopenharmony_ci        NULL;
13953a5a1b3Sopenharmony_ci}
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ciint pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, bool overwrite) {
14253a5a1b3Sopenharmony_ci    TDB_DATA tdb_key, tdb_data;
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_ci    pa_assert(db);
14553a5a1b3Sopenharmony_ci    pa_assert(key);
14653a5a1b3Sopenharmony_ci    pa_assert(data);
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci    return tdb_store(MAKE_TDB_CONTEXT(db),
14953a5a1b3Sopenharmony_ci                      *datum_to_tdb(&tdb_key, key),
15053a5a1b3Sopenharmony_ci                      *datum_to_tdb(&tdb_data, data),
15153a5a1b3Sopenharmony_ci                     overwrite ? TDB_REPLACE : TDB_INSERT) != 0 ? -1 : 0;
15253a5a1b3Sopenharmony_ci}
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ciint pa_database_unset(pa_database *db, const pa_datum *key) {
15553a5a1b3Sopenharmony_ci    TDB_DATA tdb_key;
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_ci    pa_assert(db);
15853a5a1b3Sopenharmony_ci    pa_assert(key);
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    return tdb_delete(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)) != 0 ? -1 : 0;
16153a5a1b3Sopenharmony_ci}
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ciint pa_database_clear(pa_database *db) {
16453a5a1b3Sopenharmony_ci    pa_assert(db);
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    return tdb_wipe_all(MAKE_TDB_CONTEXT(db)) != 0 ? -1 : 0;
16753a5a1b3Sopenharmony_ci}
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_cisigned pa_database_size(pa_database *db) {
17053a5a1b3Sopenharmony_ci    TDB_DATA tdb_key;
17153a5a1b3Sopenharmony_ci    unsigned n = 0;
17253a5a1b3Sopenharmony_ci
17353a5a1b3Sopenharmony_ci    pa_assert(db);
17453a5a1b3Sopenharmony_ci
17553a5a1b3Sopenharmony_ci    /* This sucks */
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci    tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci    while (tdb_key.dptr) {
18053a5a1b3Sopenharmony_ci        TDB_DATA next;
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci        n++;
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci        next = tdb_nextkey(MAKE_TDB_CONTEXT(db), tdb_key);
18553a5a1b3Sopenharmony_ci        free(tdb_key.dptr);
18653a5a1b3Sopenharmony_ci        tdb_key = next;
18753a5a1b3Sopenharmony_ci    }
18853a5a1b3Sopenharmony_ci
18953a5a1b3Sopenharmony_ci    return (signed) n;
19053a5a1b3Sopenharmony_ci}
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_cipa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) {
19353a5a1b3Sopenharmony_ci    TDB_DATA tdb_key, tdb_data;
19453a5a1b3Sopenharmony_ci
19553a5a1b3Sopenharmony_ci    pa_assert(db);
19653a5a1b3Sopenharmony_ci    pa_assert(key);
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci    tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db));
19953a5a1b3Sopenharmony_ci
20053a5a1b3Sopenharmony_ci    if (!tdb_key.dptr)
20153a5a1b3Sopenharmony_ci        return NULL;
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    if (data) {
20453a5a1b3Sopenharmony_ci        tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci        if (!tdb_data.dptr) {
20753a5a1b3Sopenharmony_ci            free(tdb_key.dptr);
20853a5a1b3Sopenharmony_ci            return NULL;
20953a5a1b3Sopenharmony_ci        }
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci        datum_from_tdb(data, &tdb_data);
21253a5a1b3Sopenharmony_ci    }
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci    datum_from_tdb(key, &tdb_key);
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci    return key;
21753a5a1b3Sopenharmony_ci}
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_cipa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) {
22053a5a1b3Sopenharmony_ci    TDB_DATA tdb_key, tdb_data;
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci    pa_assert(db);
22353a5a1b3Sopenharmony_ci    pa_assert(key);
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci    tdb_key = tdb_nextkey(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key));
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    if (!tdb_key.dptr)
22853a5a1b3Sopenharmony_ci        return NULL;
22953a5a1b3Sopenharmony_ci
23053a5a1b3Sopenharmony_ci    if (data) {
23153a5a1b3Sopenharmony_ci        tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key);
23253a5a1b3Sopenharmony_ci
23353a5a1b3Sopenharmony_ci        if (!tdb_data.dptr) {
23453a5a1b3Sopenharmony_ci            free(tdb_key.dptr);
23553a5a1b3Sopenharmony_ci            return NULL;
23653a5a1b3Sopenharmony_ci        }
23753a5a1b3Sopenharmony_ci
23853a5a1b3Sopenharmony_ci        datum_from_tdb(data, &tdb_data);
23953a5a1b3Sopenharmony_ci    }
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    datum_from_tdb(next, &tdb_key);
24253a5a1b3Sopenharmony_ci
24353a5a1b3Sopenharmony_ci    return next;
24453a5a1b3Sopenharmony_ci}
24553a5a1b3Sopenharmony_ci
24653a5a1b3Sopenharmony_ciint pa_database_sync(pa_database *db) {
24753a5a1b3Sopenharmony_ci    pa_assert(db);
24853a5a1b3Sopenharmony_ci
24953a5a1b3Sopenharmony_ci    return 0;
25053a5a1b3Sopenharmony_ci}
251