1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Nokia Corporation 5 Contact: Maemo Multimedia <multimedia@maemo.org> 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as 9 published by the Free Software Foundation; either version 2.1 of the 10 License, or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <errno.h> 26#include <sys/types.h> 27#include <unistd.h> 28#include <stdio.h> 29 30#include <pulse/xmalloc.h> 31#include <pulsecore/core-util.h> 32#include <pulsecore/log.h> 33#include <pulsecore/core-error.h> 34#include <pulsecore/hashmap.h> 35 36#include "database.h" 37 38typedef struct simple_data { 39 char *filename; 40 char *tmp_filename; 41 pa_hashmap *map; 42 bool read_only; 43} simple_data; 44 45typedef struct entry { 46 pa_datum key; 47 pa_datum data; 48} entry; 49 50void pa_datum_free(pa_datum *d) { 51 pa_assert(d); 52 53 pa_xfree(d->data); 54 d->data = NULL; 55 d->size = 0; 56} 57 58static int compare_func(const void *a, const void *b) { 59 const pa_datum *aa, *bb; 60 61 aa = (const pa_datum*)a; 62 bb = (const pa_datum*)b; 63 64 if (aa->size != bb->size) 65 return aa->size > bb->size ? 1 : -1; 66 67 return memcmp(aa->data, bb->data, aa->size); 68} 69 70/* pa_idxset_string_hash_func modified for our use */ 71static unsigned hash_func(const void *p) { 72 const pa_datum *d; 73 unsigned hash = 0; 74 const char *c; 75 unsigned i; 76 77 d = (const pa_datum*)p; 78 c = d->data; 79 80 for (i = 0; i < d->size; i++) { 81 hash = 31 * hash + (unsigned) *c; 82 c++; 83 } 84 85 return hash; 86} 87 88static entry* new_entry(const pa_datum *key, const pa_datum *data) { 89 entry *e; 90 91 pa_assert(key); 92 pa_assert(data); 93 94 e = pa_xnew0(entry, 1); 95 e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL; 96 e->key.size = key->size; 97 e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL; 98 e->data.size = data->size; 99 return e; 100} 101 102static void free_entry(entry *e) { 103 if (e) { 104 if (e->key.data) 105 pa_xfree(e->key.data); 106 if (e->data.data) 107 pa_xfree(e->data.data); 108 pa_xfree(e); 109 } 110} 111 112static int read_uint(FILE *f, uint32_t *res) { 113 size_t items = 0; 114 uint8_t values[4]; 115 uint32_t tmp; 116 int i; 117 118 items = fread(&values, sizeof(values), sizeof(uint8_t), f); 119 120 if (feof(f)) /* EOF */ 121 return 0; 122 123 if (ferror(f)) 124 return -1; 125 126 for (i = 0; i < 4; ++i) { 127 tmp = values[i]; 128 *res += (tmp << (i*8)); 129 } 130 131 return items; 132} 133 134static int read_data(FILE *f, void **data, ssize_t *length) { 135 size_t items = 0; 136 uint32_t data_len = 0; 137 138 pa_assert(f); 139 140 *data = NULL; 141 *length = 0; 142 143 if ((items = read_uint(f, &data_len)) <= 0) 144 return -1; 145 146 if (data_len > 0) { 147 *data = pa_xmalloc0(data_len); 148 items = fread(*data, data_len, 1, f); 149 150 if (feof(f)) /* EOF */ 151 goto reset; 152 153 if (ferror(f)) 154 goto reset; 155 156 *length = data_len; 157 158 } else { /* no data? */ 159 return -1; 160 } 161 162 return 0; 163 164reset: 165 pa_xfree(*data); 166 *data = NULL; 167 *length = 0; 168 return -1; 169} 170 171static int fill_data(simple_data *db, FILE *f) { 172 pa_datum key; 173 pa_datum data; 174 void *d = NULL; 175 ssize_t l = 0; 176 bool append = false; 177 enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY; 178 179 pa_assert(db); 180 pa_assert(db->map); 181 182 errno = 0; 183 184 key.size = 0; 185 key.data = NULL; 186 187 while (!read_data(f, &d, &l)) { 188 189 switch (field) { 190 case FIELD_KEY: 191 key.data = d; 192 key.size = l; 193 field = FIELD_DATA; 194 break; 195 case FIELD_DATA: 196 data.data = d; 197 data.size = l; 198 append = true; 199 break; 200 } 201 202 if (append) { 203 entry *e = pa_xnew0(entry, 1); 204 e->key.data = key.data; 205 e->key.size = key.size; 206 e->data.data = data.data; 207 e->data.size = data.size; 208 pa_hashmap_put(db->map, &e->key, e); 209 append = false; 210 field = FIELD_KEY; 211 } 212 } 213 214 if (ferror(f)) { 215 pa_log_warn("read error. %s", pa_cstrerror(errno)); 216 pa_database_clear((pa_database*)db); 217 } 218 219 if (field == FIELD_DATA && d) 220 pa_xfree(d); 221 222 return pa_hashmap_size(db->map); 223} 224 225const char* pa_database_get_filename_suffix(void) { 226 return ".simple"; 227} 228 229pa_database* pa_database_open_internal(const char *path, bool for_write) { 230 FILE *f; 231 simple_data *db; 232 233 pa_assert(path); 234 235 errno = 0; 236 237 f = pa_fopen_cloexec(path, "r"); 238 239 if (f || errno == ENOENT) { /* file not found is ok */ 240 db = pa_xnew0(simple_data, 1); 241 db->map = pa_hashmap_new_full(hash_func, compare_func, NULL, (pa_free_cb_t) free_entry); 242 db->filename = pa_xstrdup(path); 243 db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename); 244 db->read_only = !for_write; 245 246 if (f) { 247 fill_data(db, f); 248 fclose(f); 249 } 250 } else { 251 if (errno == 0) 252 errno = EIO; 253 db = NULL; 254 } 255 256 return (pa_database*) db; 257} 258 259void pa_database_close(pa_database *database) { 260 simple_data *db = (simple_data*)database; 261 pa_assert(db); 262 263 pa_database_sync(database); 264 pa_xfree(db->filename); 265 pa_xfree(db->tmp_filename); 266 pa_hashmap_free(db->map); 267 pa_xfree(db); 268} 269 270pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) { 271 simple_data *db = (simple_data*)database; 272 entry *e; 273 274 pa_assert(db); 275 pa_assert(key); 276 pa_assert(data); 277 278 e = pa_hashmap_get(db->map, key); 279 280 if (!e) 281 return NULL; 282 283 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL; 284 data->size = e->data.size; 285 286 return data; 287} 288 289int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, bool overwrite) { 290 simple_data *db = (simple_data*)database; 291 entry *e; 292 int ret = 0; 293 294 pa_assert(db); 295 pa_assert(key); 296 pa_assert(data); 297 298 if (db->read_only) 299 return -1; 300 301 e = new_entry(key, data); 302 303 if (pa_hashmap_put(db->map, &e->key, e) < 0) { 304 /* entry with same key exists in hashmap */ 305 entry *r; 306 if (overwrite) { 307 r = pa_hashmap_remove(db->map, key); 308 pa_hashmap_put(db->map, &e->key, e); 309 } else { 310 /* won't overwrite, so clean new entry */ 311 r = e; 312 ret = -1; 313 } 314 315 free_entry(r); 316 } 317 318 return ret; 319} 320 321int pa_database_unset(pa_database *database, const pa_datum *key) { 322 simple_data *db = (simple_data*)database; 323 324 pa_assert(db); 325 pa_assert(key); 326 327 return pa_hashmap_remove_and_free(db->map, key); 328} 329 330int pa_database_clear(pa_database *database) { 331 simple_data *db = (simple_data*)database; 332 333 pa_assert(db); 334 335 pa_hashmap_remove_all(db->map); 336 337 return 0; 338} 339 340signed pa_database_size(pa_database *database) { 341 simple_data *db = (simple_data*)database; 342 pa_assert(db); 343 344 return (signed) pa_hashmap_size(db->map); 345} 346 347pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) { 348 simple_data *db = (simple_data*)database; 349 entry *e; 350 351 pa_assert(db); 352 pa_assert(key); 353 354 e = pa_hashmap_first(db->map); 355 356 if (!e) 357 return NULL; 358 359 key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL; 360 key->size = e->key.size; 361 362 if (data) { 363 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL; 364 data->size = e->data.size; 365 } 366 367 return key; 368} 369 370pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) { 371 simple_data *db = (simple_data*)database; 372 entry *e; 373 entry *search; 374 void *state; 375 bool pick_now; 376 377 pa_assert(db); 378 pa_assert(next); 379 380 if (!key) 381 return pa_database_first(database, next, data); 382 383 search = pa_hashmap_get(db->map, key); 384 385 state = NULL; 386 pick_now = false; 387 388 while ((e = pa_hashmap_iterate(db->map, &state, NULL))) { 389 if (pick_now) 390 break; 391 392 if (search == e) 393 pick_now = true; 394 } 395 396 if (!pick_now || !e) 397 return NULL; 398 399 next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL; 400 next->size = e->key.size; 401 402 if (data) { 403 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL; 404 data->size = e->data.size; 405 } 406 407 return next; 408} 409 410static int write_uint(FILE *f, const uint32_t num) { 411 size_t items; 412 uint8_t values[4]; 413 int i; 414 errno = 0; 415 416 for (i = 0; i < 4; i++) 417 values[i] = (num >> (i*8)) & 0xFF; 418 419 items = fwrite(&values, sizeof(values), sizeof(uint8_t), f); 420 421 if (ferror(f)) 422 return -1; 423 424 return items; 425} 426 427static int write_data(FILE *f, void *data, const size_t length) { 428 size_t items; 429 uint32_t len; 430 431 len = length; 432 if ((items = write_uint(f, len)) <= 0) 433 return -1; 434 435 items = fwrite(data, length, 1, f); 436 437 if (ferror(f) || items != 1) 438 return -1; 439 440 return 0; 441} 442 443static int write_entry(FILE *f, const entry *e) { 444 pa_assert(f); 445 pa_assert(e); 446 447 if (write_data(f, e->key.data, e->key.size) < 0) 448 return -1; 449 if (write_data(f, e->data.data, e->data.size) < 0) 450 return -1; 451 452 return 0; 453} 454 455int pa_database_sync(pa_database *database) { 456 simple_data *db = (simple_data*)database; 457 FILE *f; 458 void *state; 459 entry *e; 460 461 pa_assert(db); 462 463 if (db->read_only) 464 return 0; 465 466 errno = 0; 467 468 f = pa_fopen_cloexec(db->tmp_filename, "w"); 469 470 if (!f) 471 goto fail; 472 473 state = NULL; 474 while((e = pa_hashmap_iterate(db->map, &state, NULL))) { 475 if (write_entry(f, e) < 0) { 476 pa_log_warn("error while writing to file. %s", pa_cstrerror(errno)); 477 goto fail; 478 } 479 } 480 481 fclose(f); 482 f = NULL; 483 484 if (rename(db->tmp_filename, db->filename) < 0) { 485 pa_log_warn("error while renaming file. %s", pa_cstrerror(errno)); 486 goto fail; 487 } 488 489 return 0; 490 491fail: 492 if (f) 493 fclose(f); 494 return -1; 495} 496