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