1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as 8 published by the Free Software Foundation; either version 2.1 of the 9 License, or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <fcntl.h> 25#include <unistd.h> 26#include <errno.h> 27 28/* Some versions of tdb lack inclusion of signal.h in the header files but use sigatomic_t */ 29#include <signal.h> 30#include <tdb.h> 31 32#include <pulse/xmalloc.h> 33#include <pulsecore/core-util.h> 34#include <pulsecore/log.h> 35 36#include "database.h" 37 38#define MAKE_TDB_CONTEXT(x) ((struct tdb_context*) (x)) 39 40static inline TDB_DATA* datum_to_tdb(TDB_DATA *to, const pa_datum *from) { 41 pa_assert(from); 42 pa_assert(to); 43 44 to->dptr = from->data; 45 to->dsize = from->size; 46 47 return to; 48} 49 50static inline pa_datum* datum_from_tdb(pa_datum *to, const TDB_DATA *from) { 51 pa_assert(from); 52 pa_assert(to); 53 54 to->data = from->dptr; 55 to->size = from->dsize; 56 57 return to; 58} 59 60void pa_datum_free(pa_datum *d) { 61 pa_assert(d); 62 63 free(d->data); /* tdb uses raw malloc/free hence we should do that here, too */ 64 pa_zero(d); 65} 66 67static struct tdb_context *tdb_open_cloexec( 68 const char *name, 69 int hash_size, 70 int tdb_flags, 71 int open_flags, 72 mode_t mode) { 73 74 /* Mimics pa_open_cloexec() */ 75 76 struct tdb_context *c; 77 78#ifdef O_NOCTTY 79 open_flags |= O_NOCTTY; 80#endif 81 82#ifdef O_CLOEXEC 83 errno = 0; 84 if ((c = tdb_open(name, hash_size, tdb_flags, open_flags | O_CLOEXEC, mode))) 85 goto finish; 86 87 if (errno != EINVAL) 88 return NULL; 89#endif 90 91 errno = 0; 92 if (!(c = tdb_open(name, hash_size, tdb_flags, open_flags, mode))) 93 return NULL; 94 95finish: 96 pa_make_fd_cloexec(tdb_fd(c)); 97 return c; 98} 99 100const char* pa_database_get_filename_suffix(void) { 101 return ".tdb"; 102} 103 104pa_database* pa_database_open_internal(const char *path, bool for_write) { 105 struct tdb_context *c; 106 107 pa_assert(path); 108 109 if ((c = tdb_open_cloexec(path, 0, TDB_NOSYNC|TDB_NOLOCK, (for_write ? O_RDWR|O_CREAT : O_RDONLY), 0644))) 110 pa_log_debug("Opened TDB database '%s'", path); 111 112 if (!c) { 113 if (errno == 0) 114 errno = EIO; 115 return NULL; 116 } 117 118 return (pa_database*) c; 119} 120 121void pa_database_close(pa_database *db) { 122 pa_assert(db); 123 124 tdb_close(MAKE_TDB_CONTEXT(db)); 125} 126 127pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) { 128 TDB_DATA tdb_key, tdb_data; 129 130 pa_assert(db); 131 pa_assert(key); 132 pa_assert(data); 133 134 tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)); 135 136 return tdb_data.dptr ? 137 datum_from_tdb(data, &tdb_data) : 138 NULL; 139} 140 141int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, bool overwrite) { 142 TDB_DATA tdb_key, tdb_data; 143 144 pa_assert(db); 145 pa_assert(key); 146 pa_assert(data); 147 148 return tdb_store(MAKE_TDB_CONTEXT(db), 149 *datum_to_tdb(&tdb_key, key), 150 *datum_to_tdb(&tdb_data, data), 151 overwrite ? TDB_REPLACE : TDB_INSERT) != 0 ? -1 : 0; 152} 153 154int pa_database_unset(pa_database *db, const pa_datum *key) { 155 TDB_DATA tdb_key; 156 157 pa_assert(db); 158 pa_assert(key); 159 160 return tdb_delete(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)) != 0 ? -1 : 0; 161} 162 163int pa_database_clear(pa_database *db) { 164 pa_assert(db); 165 166 return tdb_wipe_all(MAKE_TDB_CONTEXT(db)) != 0 ? -1 : 0; 167} 168 169signed pa_database_size(pa_database *db) { 170 TDB_DATA tdb_key; 171 unsigned n = 0; 172 173 pa_assert(db); 174 175 /* This sucks */ 176 177 tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db)); 178 179 while (tdb_key.dptr) { 180 TDB_DATA next; 181 182 n++; 183 184 next = tdb_nextkey(MAKE_TDB_CONTEXT(db), tdb_key); 185 free(tdb_key.dptr); 186 tdb_key = next; 187 } 188 189 return (signed) n; 190} 191 192pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) { 193 TDB_DATA tdb_key, tdb_data; 194 195 pa_assert(db); 196 pa_assert(key); 197 198 tdb_key = tdb_firstkey(MAKE_TDB_CONTEXT(db)); 199 200 if (!tdb_key.dptr) 201 return NULL; 202 203 if (data) { 204 tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key); 205 206 if (!tdb_data.dptr) { 207 free(tdb_key.dptr); 208 return NULL; 209 } 210 211 datum_from_tdb(data, &tdb_data); 212 } 213 214 datum_from_tdb(key, &tdb_key); 215 216 return key; 217} 218 219pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) { 220 TDB_DATA tdb_key, tdb_data; 221 222 pa_assert(db); 223 pa_assert(key); 224 225 tdb_key = tdb_nextkey(MAKE_TDB_CONTEXT(db), *datum_to_tdb(&tdb_key, key)); 226 227 if (!tdb_key.dptr) 228 return NULL; 229 230 if (data) { 231 tdb_data = tdb_fetch(MAKE_TDB_CONTEXT(db), tdb_key); 232 233 if (!tdb_data.dptr) { 234 free(tdb_key.dptr); 235 return NULL; 236 } 237 238 datum_from_tdb(data, &tdb_data); 239 } 240 241 datum_from_tdb(next, &tdb_key); 242 243 return next; 244} 245 246int pa_database_sync(pa_database *db) { 247 pa_assert(db); 248 249 return 0; 250} 251