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