Imported Upstream version 2
[platform/upstream/buxton.git] / src / db / gdbm.c
1 /*
2  * This file is part of buxton.
3  *
4  * Copyright (C) 2013 Intel Corporation
5  *
6  * buxton 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
9  * of the License, or (at your option) any later version.
10  */
11
12 #ifdef HAVE_CONFIG_H
13         #include "config.h"
14 #endif
15
16 #include <assert.h>
17 #include <errno.h>
18 #include <gdbm.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "log.h"
23 #include "hashmap.h"
24 #include "serialize.h"
25 #include "util.h"
26
27 /**
28  * GDBM Database Module
29  */
30
31
32 static Hashmap *_resources = NULL;
33
34 static char *key_get_name(BuxtonString *key)
35 {
36         char *c;
37
38         c = strchr(key->value, 0);
39         if (!c) {
40                 return NULL;
41         }
42         if (c - (key->value + (key->length - 1)) >= 0) {
43                 return NULL;
44         }
45         c++;
46
47         return c;
48 }
49
50 /* Open or create databases on the fly */
51 static GDBM_FILE db_for_resource(BuxtonLayer *layer)
52 {
53         GDBM_FILE db;
54         _cleanup_free_ char *path = NULL;
55         char *name = NULL;
56         int r;
57
58         assert(layer);
59         assert(_resources);
60
61         if (layer->type == LAYER_USER) {
62                 r = asprintf(&name, "%s-%d", layer->name.value, layer->uid);
63         } else {
64                 r = asprintf(&name, "%s", layer->name.value);
65         }
66         if (r == -1) {
67                 abort();
68         }
69
70         db = hashmap_get(_resources, name);
71         if (!db) {
72                 path = get_layer_path(layer);
73                 if (!path) {
74                         abort();
75                 }
76                 db = gdbm_open(path, 0, GDBM_WRCREAT, 0600, NULL);
77                 if (!db) {
78                         free(name);
79                         buxton_log("Couldn't create db for path: %s\n", path);
80                         return 0;
81                 }
82                 r = hashmap_put(_resources, name, db);
83                 if (r != 1) {
84                         abort();
85                 }
86         } else {
87                 db = (GDBM_FILE) hashmap_get(_resources, name);
88                 free(name);
89         }
90
91         return db;
92 }
93
94 static int set_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data,
95                       BuxtonString *label)
96 {
97         GDBM_FILE db;
98         int ret = -1;
99         datum key_data;
100         datum cvalue = {0};
101         datum value;
102         _cleanup_free_ uint8_t *data_store = NULL;
103         size_t size;
104         uint32_t sz;
105         BuxtonData cdata = {0};
106         BuxtonString clabel;
107
108         assert(layer);
109         assert(key);
110         assert(label);
111
112         if (key->name.value) {
113                 sz = key->group.length + key->name.length;
114                 key_data.dptr = malloc(sz);
115                 if (!key_data.dptr) {
116                         abort();
117                 }
118
119                 /* size is string\0string\0 so just write, bonus for
120                    nil seperator being added without extra work */
121                 key_data.dsize = (int)sz;
122                 memcpy(key_data.dptr, key->group.value, key->group.length);
123                 memcpy(key_data.dptr + key->group.length, key->name.value,
124                        key->name.length);
125         } else {
126                 key_data.dptr = malloc(key->group.length);
127                 if (!key_data.dptr) {
128                         abort();
129                 }
130
131                 memcpy(key_data.dptr, key->group.value, key->group.length);
132                 key_data.dsize = (int)key->group.length;
133         }
134
135         db = db_for_resource(layer);
136         if (!db) {
137                 ret = ENOENT;
138                 goto end;
139         }
140
141         /* set_label will pass a NULL for data */
142         if (!data) {
143                 cvalue = gdbm_fetch(db, key_data);
144                 if (cvalue.dsize < 0 || cvalue.dptr == NULL) {
145                         ret = ENOENT;
146                         goto end;
147                 }
148
149                 data_store = (uint8_t*)cvalue.dptr;
150                 buxton_deserialize(data_store, &cdata, &clabel);
151                 free(clabel.value);
152                 data = &cdata;
153                 data_store = NULL;
154         }
155
156         size = buxton_serialize(data, label, &data_store);
157
158         value.dptr = (char *)data_store;
159         value.dsize = (int)size;
160         ret = gdbm_store(db, key_data, value, GDBM_REPLACE);
161         assert(ret == 0);
162
163 end:
164         if (cdata.type == STRING) {
165                 free(cdata.store.d_string.value);
166         }
167         free(key_data.dptr);
168         free(cvalue.dptr);
169
170         return ret;
171 }
172
173 static int get_value(BuxtonLayer *layer, _BuxtonKey *key, BuxtonData *data,
174                       BuxtonString *label)
175 {
176         GDBM_FILE db;
177         datum key_data;
178         datum value;
179         uint8_t *data_store = NULL;
180         int ret;
181         uint32_t sz;
182
183         assert(layer);
184
185         if (key->name.value) {
186                 sz = key->group.length + key->name.length;
187                 key_data.dptr = malloc(sz);
188                 if (!key_data.dptr) {
189                         abort();
190                 }
191
192                 /* size is string\0string\0 so just write, bonus for
193                    nil seperator being added without extra work */
194                 key_data.dsize = (int)sz;
195                 memcpy(key_data.dptr, key->group.value, key->group.length);
196                 memcpy(key_data.dptr + key->group.length, key->name.value,
197                        key->name.length);
198         } else {
199                 key_data.dptr = malloc(key->group.length);
200                 if (!key_data.dptr) {
201                         abort();
202                 }
203
204                 memcpy(key_data.dptr, key->group.value, key->group.length);
205                 key_data.dsize = (int)key->group.length;
206         }
207
208         memzero(&value, sizeof(datum));
209         db = db_for_resource(layer);
210         if (!db) {
211                 /*
212                  * Set negative here to indicate layer not found
213                  * rather than key not found, optimization for
214                  * set value
215                  */
216                 ret = -ENOENT;
217                 goto end;
218         }
219
220         value = gdbm_fetch(db, key_data);
221         if (value.dsize < 0 || value.dptr == NULL) {
222                 ret = ENOENT;
223                 goto end;
224         }
225
226         data_store = (uint8_t*)value.dptr;
227         buxton_deserialize(data_store, data, label);
228
229         if (data->type != key->type) {
230                 free(label->value);
231                 label->value = NULL;
232                 if (data->type == STRING) {
233                         free(data->store.d_string.value);
234                         data->store.d_string.value = NULL;
235                 }
236                 ret = EINVAL;
237                 goto end;
238         }
239         ret = 0;
240
241 end:
242         free(key_data.dptr);
243         free(value.dptr);
244         data_store = NULL;
245
246         return ret;
247 }
248
249 static int unset_value(BuxtonLayer *layer,
250                         _BuxtonKey *key,
251                         __attribute__((unused)) BuxtonData *data,
252                         __attribute__((unused)) BuxtonString *label)
253 {
254         GDBM_FILE db;
255         datum key_data;
256         int ret;
257         uint32_t sz;
258
259         assert(layer);
260         assert(key);
261
262         if (key->name.value) {
263                 sz = key->group.length + key->name.length;
264                 key_data.dptr = malloc(sz);
265                 if (!key_data.dptr) {
266                         abort();
267                 }
268
269                 /* size is string\0string\0 so just write, bonus for
270                    nil seperator being added without extra work */
271                 key_data.dsize = (int)sz;
272                 memcpy(key_data.dptr, key->group.value, key->group.length);
273                 memcpy(key_data.dptr + key->group.length, key->name.value,
274                        key->name.length);
275         } else {
276                 key_data.dptr = malloc(key->group.length);
277                 if (!key_data.dptr) {
278                         abort();
279                 }
280
281                 memcpy(key_data.dptr, key->group.value, key->group.length);
282                 key_data.dsize = (int)key->group.length;
283         }
284
285         db = db_for_resource(layer);
286         if (!db) {
287                 ret = ENOENT;
288                 goto end;
289         }
290
291         /* Negative value means the key wasn't found */
292         ret = gdbm_delete(db, key_data);
293         if (ret == -1) {
294                 ret = ENOENT;
295         }
296
297 end:
298         free(key_data.dptr);
299
300         return ret;
301 }
302
303 static bool list_keys(BuxtonLayer *layer,
304                       BuxtonArray **list)
305 {
306         GDBM_FILE db;
307         datum key, nextkey;
308         BuxtonArray *k_list = NULL;
309         BuxtonData *current = NULL;
310         BuxtonString in_key;
311         char *name;
312         bool ret = false;
313
314         assert(layer);
315
316         db = db_for_resource(layer);
317         if (!db) {
318                 goto end;
319         }
320
321         k_list = buxton_array_new();
322         key = gdbm_firstkey(db);
323         /* Iterate through all of the keys */
324         while (key.dptr) {
325                 /* Split the key name from the rest of the key */
326                 in_key.value = (char*)key.dptr;
327                 in_key.length = (uint32_t)key.dsize;
328                 name = key_get_name(&in_key);
329                 if (!name) {
330                         continue;
331                 }
332
333                 current = malloc0(sizeof(BuxtonData));
334                 if (!current) {
335                         abort();
336                 }
337                 current->type = STRING;
338                 current->store.d_string.value = strdup(name);
339                 if (!current->store.d_string.value) {
340                         abort();
341                 }
342                 current->store.d_string.length = (uint32_t)strlen(name) + 1;
343                 if (!buxton_array_add(k_list, current)) {
344                         abort();
345                 }
346
347                 /* Visit the next key */
348                 nextkey = gdbm_nextkey(db, key);
349                 free(key.dptr);
350                 key = nextkey;
351         }
352
353         /* Pass ownership of the array to the caller */
354         *list = k_list;
355         ret = true;
356
357 end:
358         if (!ret && k_list) {
359                 for (uint16_t i = 0; i < k_list->len; i++) {
360                         current = buxton_array_get(k_list, i);
361                         if (!current) {
362                                 break;
363                         }
364                         free(current->store.d_string.value);
365                         free(current);
366                 }
367                 buxton_array_free(&k_list, NULL);
368         }
369         return ret;
370 }
371
372 _bx_export_ void buxton_module_destroy(void)
373 {
374         const char *key;
375         Iterator iterator;
376         GDBM_FILE db;
377
378         /* close all gdbm handles */
379         HASHMAP_FOREACH_KEY(db, key, _resources, iterator) {
380                 hashmap_remove(_resources, key);
381                 gdbm_close(db);
382                 free((void *)key);
383         }
384         hashmap_free(_resources);
385         _resources = NULL;
386 }
387
388 _bx_export_ bool buxton_module_init(BuxtonBackend *backend)
389 {
390
391         assert(backend);
392
393         /* Point the struct methods back to our own */
394         backend->set_value = &set_value;
395         backend->get_value = &get_value;
396         backend->list_keys = &list_keys;
397         backend->unset_value = &unset_value;
398         backend->create_db = (module_db_init_func) &db_for_resource;
399
400         _resources = hashmap_new(string_hash_func, string_compare_func);
401         if (!_resources) {
402                 abort();
403         }
404
405         return true;
406 }
407
408 /*
409  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
410  *
411  * Local variables:
412  * c-basic-offset: 8
413  * tab-width: 8
414  * indent-tabs-mode: t
415  * End:
416  *
417  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
418  * :indentSize=8:tabSize=8:noTabs=false:
419  */