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