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 <errno.h> 25#include <gdbm.h> 26 27#include <pulse/xmalloc.h> 28#include <pulsecore/core-util.h> 29#include <pulsecore/log.h> 30 31#include "database.h" 32 33#define MAKE_GDBM_FILE(x) ((GDBM_FILE) (x)) 34 35static inline datum* datum_to_gdbm(datum *to, const pa_datum *from) { 36 pa_assert(from); 37 pa_assert(to); 38 39 to->dptr = from->data; 40 to->dsize = from->size; 41 42 return to; 43} 44 45static inline pa_datum* datum_from_gdbm(pa_datum *to, const datum *from) { 46 pa_assert(from); 47 pa_assert(to); 48 49 to->data = from->dptr; 50 to->size = from->dsize; 51 52 return to; 53} 54 55void pa_datum_free(pa_datum *d) { 56 pa_assert(d); 57 58 free(d->data); /* gdbm uses raw malloc/free hence we should do that here, too */ 59 pa_zero(d); 60} 61 62const char* pa_database_get_filename_suffix(void) { 63 return ".gdbm"; 64} 65 66pa_database* pa_database_open_internal(const char *path, bool for_write) { 67 GDBM_FILE f; 68 int gdbm_cache_size; 69 70 pa_assert(path); 71 72 errno = 0; 73 74 /* We need to set the block size explicitly here, since otherwise 75 * gdbm takes the native block size of the underlying file system 76 * which might be incredibly large. */ 77 f = gdbm_open((char*) path, 1024, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL); 78 79 if (f) 80 pa_log_debug("Opened GDBM database '%s'", path); 81 82 if (!f) { 83 if (errno == 0) 84 errno = EIO; 85 return NULL; 86 } 87 88 /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */ 89 gdbm_cache_size = 10; 90 gdbm_setopt(f, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size)); 91 92 return (pa_database*) f; 93} 94 95void pa_database_close(pa_database *db) { 96 pa_assert(db); 97 98 gdbm_close(MAKE_GDBM_FILE(db)); 99} 100 101pa_datum* pa_database_get(pa_database *db, const pa_datum *key, pa_datum* data) { 102 datum gdbm_key, gdbm_data; 103 104 pa_assert(db); 105 pa_assert(key); 106 pa_assert(data); 107 108 gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key)); 109 110 return gdbm_data.dptr ? 111 datum_from_gdbm(data, &gdbm_data) : 112 NULL; 113} 114 115int pa_database_set(pa_database *db, const pa_datum *key, const pa_datum* data, bool overwrite) { 116 datum gdbm_key, gdbm_data; 117 118 pa_assert(db); 119 pa_assert(key); 120 pa_assert(data); 121 122 return gdbm_store(MAKE_GDBM_FILE(db), 123 *datum_to_gdbm(&gdbm_key, key), 124 *datum_to_gdbm(&gdbm_data, data), 125 overwrite ? GDBM_REPLACE : GDBM_INSERT) != 0 ? -1 : 0; 126} 127 128int pa_database_unset(pa_database *db, const pa_datum *key) { 129 datum gdbm_key; 130 131 pa_assert(db); 132 pa_assert(key); 133 134 return gdbm_delete(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key)) != 0 ? -1 : 0; 135} 136 137int pa_database_clear(pa_database *db) { 138 datum gdbm_key; 139 140 pa_assert(db); 141 142 gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db)); 143 144 while (gdbm_key.dptr) { 145 datum next; 146 147 next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key); 148 149 gdbm_delete(MAKE_GDBM_FILE(db), gdbm_key); 150 151 free(gdbm_key.dptr); 152 gdbm_key = next; 153 } 154 155 return gdbm_reorganize(MAKE_GDBM_FILE(db)) == 0 ? 0 : -1; 156} 157 158signed pa_database_size(pa_database *db) { 159 datum gdbm_key; 160 unsigned n = 0; 161 162 pa_assert(db); 163 164 /* This sucks */ 165 166 gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db)); 167 168 while (gdbm_key.dptr) { 169 datum next; 170 171 n++; 172 173 next = gdbm_nextkey(MAKE_GDBM_FILE(db), gdbm_key); 174 free(gdbm_key.dptr); 175 gdbm_key = next; 176 } 177 178 return (signed) n; 179} 180 181pa_datum* pa_database_first(pa_database *db, pa_datum *key, pa_datum *data) { 182 datum gdbm_key, gdbm_data; 183 184 pa_assert(db); 185 pa_assert(key); 186 187 gdbm_key = gdbm_firstkey(MAKE_GDBM_FILE(db)); 188 189 if (!gdbm_key.dptr) 190 return NULL; 191 192 if (data) { 193 gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key); 194 195 if (!gdbm_data.dptr) { 196 free(gdbm_key.dptr); 197 return NULL; 198 } 199 200 datum_from_gdbm(data, &gdbm_data); 201 } 202 203 datum_from_gdbm(key, &gdbm_key); 204 205 return key; 206} 207 208pa_datum* pa_database_next(pa_database *db, const pa_datum *key, pa_datum *next, pa_datum *data) { 209 datum gdbm_key, gdbm_data; 210 211 pa_assert(db); 212 pa_assert(key); 213 pa_assert(next); 214 215 if (!key) 216 return pa_database_first(db, next, data); 217 218 gdbm_key = gdbm_nextkey(MAKE_GDBM_FILE(db), *datum_to_gdbm(&gdbm_key, key)); 219 220 if (!gdbm_key.dptr) 221 return NULL; 222 223 if (data) { 224 gdbm_data = gdbm_fetch(MAKE_GDBM_FILE(db), gdbm_key); 225 226 if (!gdbm_data.dptr) { 227 free(gdbm_key.dptr); 228 return NULL; 229 } 230 231 datum_from_gdbm(data, &gdbm_data); 232 } 233 234 datum_from_gdbm(next, &gdbm_key); 235 236 return next; 237} 238 239int pa_database_sync(pa_database *db) { 240 pa_assert(db); 241 242 gdbm_sync(MAKE_GDBM_FILE(db)); 243 return 0; 244} 245