1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2006 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 *
6 * This software is available to you under a choice of one of two
7 * licenses.  You may choose to be licensed under the terms of the GNU
8 * General Public License (GPL) Version 2, available from the file
9 * COPYING in the main directory of this source tree, or the
10 * OpenIB.org BSD license below:
11 *
12 *     Redistribution and use in source and binary forms, with or
13 *     without modification, are permitted provided that the following
14 *     conditions are met:
15 *
16 *      - Redistributions of source code must retain the above
17 *        copyright notice, this list of conditions and the following
18 *        disclaimer.
19 *
20 *      - Redistributions in binary form must reproduce the above
21 *        copyright notice, this list of conditions and the following
22 *        disclaimer in the documentation and/or other materials
23 *        provided with the distribution.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 * SOFTWARE.
33 *
34 */
35
36/*
37 * Abstract:
38 * Implementation of the osm_db interface using simple text files
39 */
40
41#if HAVE_CONFIG_H
42#  include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <sys/stat.h>
46#include <sys/types.h>
47#include <stdlib.h>
48#include <string.h>
49#include <opensm/st.h>
50#include <opensm/osm_db.h>
51
52/****d* Database/OSM_DB_MAX_LINE_LEN
53 * NAME
54 * OSM_DB_MAX_LINE_LEN
55 *
56 * DESCRIPTION
57 * The Maximal line length allowed for the file
58 *
59 * SYNOPSIS
60 */
61#define OSM_DB_MAX_LINE_LEN 1024
62/**********/
63
64/****d* Database/OSM_DB_MAX_GUID_LEN
65 * NAME
66 * OSM_DB_MAX_GUID_LEN
67 *
68 * DESCRIPTION
69 * The Maximal word length allowed for the file (guid or lid)
70 *
71 * SYNOPSIS
72 */
73#define OSM_DB_MAX_GUID_LEN 32
74/**********/
75
76/****s* OpenSM: Database/osm_db_domain_imp
77 * NAME
78 * osm_db_domain_imp
79 *
80 * DESCRIPTION
81 * An implementation for domain of the database based on text files and
82 *  hash tables.
83 *
84 * SYNOPSIS
85 */
86typedef struct osm_db_domain_imp {
87	char *file_name;
88	st_table *p_hash;
89	cl_spinlock_t lock;
90} osm_db_domain_imp_t;
91/*
92 * FIELDS
93 *
94 * SEE ALSO
95 * osm_db_domain_t
96 *********/
97
98/****s* OpenSM: Database/osm_db_imp_t
99 * NAME
100 * osm_db_imp_t
101 *
102 * DESCRIPTION
103 * An implementation for file based database
104 *
105 * SYNOPSIS
106 */
107typedef struct osm_db_imp {
108	char *db_dir_name;
109} osm_db_imp_t;
110/*
111 * FIELDS
112 *
113 * db_dir_name
114 *   The directory holding the database
115 *
116 * SEE ALSO
117 * osm_db_t
118 *********/
119
120/***************************************************************************
121 ***************************************************************************/
122void osm_db_construct(IN osm_db_t * const p_db)
123{
124	memset(p_db, 0, sizeof(osm_db_t));
125	cl_list_construct(&p_db->domains);
126}
127
128/***************************************************************************
129 ***************************************************************************/
130void osm_db_domain_destroy(IN osm_db_domain_t * const p_db_domain)
131{
132	osm_db_domain_imp_t *p_domain_imp;
133	p_domain_imp = (osm_db_domain_imp_t *) p_db_domain->p_domain_imp;
134
135	osm_db_clear(p_db_domain);
136
137	cl_spinlock_destroy(&p_domain_imp->lock);
138
139	st_free_table(p_domain_imp->p_hash);
140	free(p_domain_imp->file_name);
141	free(p_domain_imp);
142}
143
144/***************************************************************************
145 ***************************************************************************/
146void osm_db_destroy(IN osm_db_t * const p_db)
147{
148	osm_db_domain_t *p_domain;
149
150	while ((p_domain = cl_list_remove_head(&p_db->domains)) != NULL) {
151		osm_db_domain_destroy(p_domain);
152		free(p_domain);
153	}
154	cl_list_destroy(&p_db->domains);
155	free(p_db->p_db_imp);
156}
157
158/***************************************************************************
159 ***************************************************************************/
160int osm_db_init(IN osm_db_t * const p_db, IN osm_log_t * p_log)
161{
162	osm_db_imp_t *p_db_imp;
163	struct stat dstat;
164
165	OSM_LOG_ENTER(p_log);
166
167	p_db_imp = (osm_db_imp_t *) malloc(sizeof(osm_db_imp_t));
168	CL_ASSERT(p_db_imp != NULL);
169
170	p_db_imp->db_dir_name = getenv("OSM_CACHE_DIR");
171	if (!p_db_imp->db_dir_name || !(*p_db_imp->db_dir_name))
172		p_db_imp->db_dir_name = OSM_DEFAULT_CACHE_DIR;
173
174	/* Create the directory if it doesn't exist */
175	/* There is a difference in creating directory between windows and linux */
176#ifdef __WIN__
177	/* Check if the directory exists. If not - create it. */
178	CreateDirectory(p_db_imp->db_dir_name, NULL);
179#else				/* __WIN__ */
180	/* make sure the directory exists */
181	if (lstat(p_db_imp->db_dir_name, &dstat)) {
182		if (mkdir(p_db_imp->db_dir_name, 0755)) {
183			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6101: "
184				"Failed to create the db directory:%s\n",
185				p_db_imp->db_dir_name);
186			OSM_LOG_EXIT(p_log);
187			return 1;
188		}
189	}
190#endif
191
192	p_db->p_log = p_log;
193	p_db->p_db_imp = (void *)p_db_imp;
194
195	cl_list_init(&p_db->domains, 5);
196
197	OSM_LOG_EXIT(p_log);
198
199	return 0;
200}
201
202/***************************************************************************
203 ***************************************************************************/
204osm_db_domain_t *osm_db_domain_init(IN osm_db_t * const p_db,
205				    IN char *domain_name)
206{
207	osm_db_domain_t *p_domain;
208	osm_db_domain_imp_t *p_domain_imp;
209	int dir_name_len;
210	osm_log_t *p_log = p_db->p_log;
211	FILE *p_file;
212
213	OSM_LOG_ENTER(p_log);
214
215	/* allocate a new domain object */
216	p_domain = (osm_db_domain_t *) malloc(sizeof(osm_db_domain_t));
217	CL_ASSERT(p_domain != NULL);
218
219	p_domain_imp =
220	    (osm_db_domain_imp_t *) malloc(sizeof(osm_db_domain_imp_t));
221	CL_ASSERT(p_domain_imp != NULL);
222
223	dir_name_len = strlen(((osm_db_imp_t *) p_db->p_db_imp)->db_dir_name);
224
225	/* set the domain file name */
226	p_domain_imp->file_name =
227	    (char *)malloc(sizeof(char) * (dir_name_len) + strlen(domain_name) +
228			   2);
229	CL_ASSERT(p_domain_imp->file_name != NULL);
230	strcpy(p_domain_imp->file_name,
231	       ((osm_db_imp_t *) p_db->p_db_imp)->db_dir_name);
232	strcat(p_domain_imp->file_name, domain_name);
233
234	/* make sure the file exists - or exit if not writable */
235	p_file = fopen(p_domain_imp->file_name, "a+");
236	if (!p_file) {
237		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6102: "
238			"Failed to open the db file:%s\n",
239			p_domain_imp->file_name);
240		free(p_domain_imp);
241		free(p_domain);
242		p_domain = NULL;
243		goto Exit;
244	}
245	fclose(p_file);
246
247	/* initialize the hash table object */
248	p_domain_imp->p_hash = st_init_strtable();
249	CL_ASSERT(p_domain_imp->p_hash != NULL);
250
251	p_domain->p_db = p_db;
252	cl_list_insert_tail(&p_db->domains, p_domain);
253	p_domain->p_domain_imp = p_domain_imp;
254	cl_spinlock_construct(&p_domain_imp->lock);
255	cl_spinlock_init(&p_domain_imp->lock);
256
257Exit:
258	OSM_LOG_EXIT(p_log);
259	return p_domain;
260}
261
262/***************************************************************************
263 ***************************************************************************/
264int osm_db_restore(IN osm_db_domain_t * p_domain)
265{
266
267	osm_log_t *p_log = p_domain->p_db->p_log;
268	osm_db_domain_imp_t *p_domain_imp =
269	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
270	FILE *p_file;
271	int status;
272	char sLine[OSM_DB_MAX_LINE_LEN];
273	boolean_t before_key;
274	char *p_first_word, *p_rest_of_line, *p_last;
275	char *p_key = NULL;
276	char *p_prev_val, *p_accum_val = NULL;
277	char *endptr = NULL;
278	unsigned int line_num;
279
280	OSM_LOG_ENTER(p_log);
281
282	/* take the lock on the domain */
283	cl_spinlock_acquire(&p_domain_imp->lock);
284
285	/* open the file - read mode */
286	p_file = fopen(p_domain_imp->file_name, "r");
287
288	if (!p_file) {
289		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6103: "
290			"Failed to open the db file:%s\n",
291			p_domain_imp->file_name);
292		status = 1;
293		goto Exit;
294	}
295
296	/* parse the file allocating new hash tables as required */
297	/*
298	   states:
299	   before_key (0) -> in_key (1)
300
301	   before_key: if a word on the first byte - it is the key. state=in_key
302	   the rest of the line is start of the value.
303	   in_key: unless the line is empty - add it (with newlines) to the value.
304	   if empty: state=before_key
305	 */
306	status = 0;
307	before_key = TRUE;
308	line_num = 0;
309	/* if we got to EOF in the middle of a key we add a last newline */
310	while ((fgets(sLine, OSM_DB_MAX_LINE_LEN, p_file) != NULL) ||
311	       ((before_key == FALSE) && strcpy(sLine, "\n"))
312	    ) {
313		line_num++;
314		if (before_key) {
315			if ((sLine[0] != ' ') && (sLine[0] != '\t')
316			    && (sLine[0] != '\n')) {
317				/* we got a new key */
318				before_key = FALSE;
319
320				/* handle the key */
321				p_first_word =
322				    strtok_r(sLine, " \t\n", &p_last);
323				if (!p_first_word) {
324					OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6104: "
325						"Failed to get key from line:%u : %s (file:%s)\n",
326						line_num, sLine,
327						p_domain_imp->file_name);
328					status = 1;
329					goto EndParsing;
330				}
331				if (strlen(p_first_word) > OSM_DB_MAX_GUID_LEN) {
332					OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 610A: "
333						"Illegal key from line:%u : %s (file:%s)\n",
334						line_num, sLine,
335						p_domain_imp->file_name);
336					status = 1;
337					goto EndParsing;
338				}
339
340				p_key =
341				    (char *)malloc(sizeof(char) *
342						   (strlen(p_first_word) + 1));
343				strcpy(p_key, p_first_word);
344
345				p_rest_of_line = strtok_r(NULL, "\n", &p_last);
346				if (p_rest_of_line != NULL) {
347					p_accum_val =
348					    (char *)malloc(sizeof(char) *
349							   (strlen
350							    (p_rest_of_line) +
351							    1));
352					strcpy(p_accum_val, p_rest_of_line);
353				} else {
354					p_accum_val = (char *)malloc(2);
355					strcpy(p_accum_val, "\0");
356				}
357			} else if (sLine[0] != '\n') {
358				OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6105: "
359					"How did we get here? line:%u : %s (file:%s)\n",
360					line_num, sLine,
361					p_domain_imp->file_name);
362				status = 1;
363				goto EndParsing;
364			}
365		} /* before key */
366		else {
367			/* we already have a key */
368
369			if (sLine[0] == '\n') {
370				/* got an end of key */
371				before_key = TRUE;
372
373				/* make sure the key was not previously used */
374				if (st_lookup(p_domain_imp->p_hash,
375					      (st_data_t) p_key,
376					      (void *) & p_prev_val)) {
377					OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6106: "
378						"Key:%s already exists in:%s with value:%s."
379						" Removing it\n",
380						p_key,
381						p_domain_imp->file_name,
382						p_prev_val);
383				} else {
384					p_prev_val = NULL;
385				}
386
387				OSM_LOG(p_log, OSM_LOG_DEBUG,
388					"Got key:%s value:%s\n", p_key,
389					p_accum_val);
390
391				/* check that the key is a number */
392				if (!strtouq(p_key, &endptr, 0)
393				    && *endptr != '\0') {
394					OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 610B: "
395						"Key:%s is invalid\n", p_key);
396				} else {
397					/* store our key and value */
398					st_insert(p_domain_imp->p_hash,
399						  (st_data_t) p_key,
400						  (st_data_t) p_accum_val);
401				}
402			} else {
403				/* accumulate into the value */
404				p_prev_val = p_accum_val;
405				p_accum_val =
406				    (char *)malloc(strlen(p_prev_val) +
407						   strlen(sLine) + 1);
408				strcpy(p_accum_val, p_prev_val);
409				free(p_prev_val);
410				strcat(p_accum_val, sLine);
411			}
412		}		/* in key */
413	}			/* while lines or last line */
414
415EndParsing:
416	fclose(p_file);
417
418Exit:
419	cl_spinlock_release(&p_domain_imp->lock);
420	OSM_LOG_EXIT(p_log);
421	return status;
422}
423
424/***************************************************************************
425 ***************************************************************************/
426static int __osm_dump_tbl_entry(st_data_t key, st_data_t val, st_data_t arg)
427{
428	FILE *p_file = (FILE *) arg;
429	char *p_key = (char *)key;
430	char *p_val = (char *)val;
431
432	fprintf(p_file, "%s %s\n\n", p_key, p_val);
433	return ST_CONTINUE;
434}
435
436int osm_db_store(IN osm_db_domain_t * p_domain)
437{
438	osm_log_t *p_log = p_domain->p_db->p_log;
439	osm_db_domain_imp_t *p_domain_imp;
440	FILE *p_file;
441	int status = 0;
442	char *p_tmp_file_name;
443
444	OSM_LOG_ENTER(p_log);
445
446	p_domain_imp = (osm_db_domain_imp_t *) p_domain->p_domain_imp;
447	p_tmp_file_name =
448	    (char *)malloc(sizeof(char) *
449			   (strlen(p_domain_imp->file_name) + 8));
450	strcpy(p_tmp_file_name, p_domain_imp->file_name);
451	strcat(p_tmp_file_name, ".tmp");
452
453	cl_spinlock_acquire(&p_domain_imp->lock);
454
455	/* open up the output file */
456	p_file = fopen(p_tmp_file_name, "w");
457	if (!p_file) {
458		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6107: "
459			"Failed to open the db file:%s for writing\n",
460			p_domain_imp->file_name);
461		status = 1;
462		goto Exit;
463	}
464
465	st_foreach(p_domain_imp->p_hash, __osm_dump_tbl_entry,
466		   (st_data_t) p_file);
467	fclose(p_file);
468
469	/* move the domain file */
470	status = remove(p_domain_imp->file_name);
471	if (status) {
472		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6109: "
473			"Failed to remove file:%s (err:%u)\n",
474			p_domain_imp->file_name, status);
475	}
476
477	status = rename(p_tmp_file_name, p_domain_imp->file_name);
478	if (status) {
479		OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 6108: "
480			"Failed to rename the db file to:%s (err:%u)\n",
481			p_domain_imp->file_name, status);
482	}
483Exit:
484	cl_spinlock_release(&p_domain_imp->lock);
485	free(p_tmp_file_name);
486	OSM_LOG_EXIT(p_log);
487	return status;
488}
489
490/***************************************************************************
491 ***************************************************************************/
492/* simply de-allocate the key and the value and return the code
493   that makes the st_foreach delete the entry */
494static int __osm_clear_tbl_entry(st_data_t key, st_data_t val, st_data_t arg)
495{
496	free((char *)key);
497	free((char *)val);
498	return ST_DELETE;
499}
500
501int osm_db_clear(IN osm_db_domain_t * p_domain)
502{
503	osm_db_domain_imp_t *p_domain_imp =
504	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
505
506	cl_spinlock_acquire(&p_domain_imp->lock);
507	st_foreach(p_domain_imp->p_hash, __osm_clear_tbl_entry,
508		   (st_data_t) NULL);
509	cl_spinlock_release(&p_domain_imp->lock);
510
511	return 0;
512}
513
514/***************************************************************************
515 ***************************************************************************/
516static int __osm_get_key_of_tbl_entry(st_data_t key, st_data_t val,
517				      st_data_t arg)
518{
519	cl_list_t *p_list = (cl_list_t *) arg;
520	cl_list_insert_tail(p_list, (void *)key);
521	return ST_CONTINUE;
522}
523
524int osm_db_keys(IN osm_db_domain_t * p_domain, OUT cl_list_t * p_key_list)
525{
526	osm_db_domain_imp_t *p_domain_imp =
527	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
528
529	cl_spinlock_acquire(&p_domain_imp->lock);
530
531	st_foreach(p_domain_imp->p_hash,
532		   __osm_get_key_of_tbl_entry, (st_data_t) p_key_list);
533
534	cl_spinlock_release(&p_domain_imp->lock);
535
536	return 0;
537}
538
539/***************************************************************************
540 ***************************************************************************/
541char *osm_db_lookup(IN osm_db_domain_t * p_domain, IN char *const p_key)
542{
543	osm_db_domain_imp_t *p_domain_imp =
544	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
545	char *p_val = NULL;
546
547	cl_spinlock_acquire(&p_domain_imp->lock);
548
549	if (!st_lookup
550	    (p_domain_imp->p_hash, (st_data_t) p_key, (void *) & p_val))
551		p_val = NULL;
552
553	cl_spinlock_release(&p_domain_imp->lock);
554
555	return p_val;
556}
557
558/***************************************************************************
559 ***************************************************************************/
560int
561osm_db_update(IN osm_db_domain_t * p_domain,
562	      IN char *const p_key, IN char *const p_val)
563{
564	osm_log_t *p_log = p_domain->p_db->p_log;
565	osm_db_domain_imp_t *p_domain_imp =
566	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
567	char *p_prev_val = NULL;
568	char *p_new_key;
569	char *p_new_val;
570
571	cl_spinlock_acquire(&p_domain_imp->lock);
572
573	if (st_lookup(p_domain_imp->p_hash,
574		      (st_data_t) p_key, (void *) & p_prev_val)) {
575		OSM_LOG(p_log, OSM_LOG_DEBUG,
576			"Key:%s previously exists in:%s with value:%s\n",
577			p_key, p_domain_imp->file_name, p_prev_val);
578		p_new_key = p_key;
579	} else {
580		/* need to allocate the key */
581		p_new_key = malloc(sizeof(char) * (strlen(p_key) + 1));
582		strcpy(p_new_key, p_key);
583	}
584
585	/* need to arange a new copy of the  value */
586	p_new_val = malloc(sizeof(char) * (strlen(p_val) + 1));
587	strcpy(p_new_val, p_val);
588
589	st_insert(p_domain_imp->p_hash, (st_data_t) p_new_key,
590		  (st_data_t) p_new_val);
591
592	if (p_prev_val)
593		free(p_prev_val);
594
595	cl_spinlock_release(&p_domain_imp->lock);
596
597	return 0;
598}
599
600/***************************************************************************
601 ***************************************************************************/
602int osm_db_delete(IN osm_db_domain_t * p_domain, IN char *const p_key)
603{
604	osm_log_t *p_log = p_domain->p_db->p_log;
605	osm_db_domain_imp_t *p_domain_imp =
606	    (osm_db_domain_imp_t *) p_domain->p_domain_imp;
607	char *p_prev_val = NULL;
608	int res;
609
610	OSM_LOG_ENTER(p_log);
611
612	cl_spinlock_acquire(&p_domain_imp->lock);
613	if (st_delete(p_domain_imp->p_hash,
614		      (void *) & p_key, (void *) & p_prev_val)) {
615		if (st_lookup(p_domain_imp->p_hash,
616			      (st_data_t) p_key, (void *) & p_prev_val)) {
617			OSM_LOG(p_log, OSM_LOG_ERROR,
618				"key:%s still exists in:%s with value:%s\n",
619				p_key, p_domain_imp->file_name, p_prev_val);
620			res = 1;
621		} else {
622			free(p_key);
623			free(p_prev_val);
624			res = 0;
625		}
626	} else {
627		OSM_LOG(p_log, OSM_LOG_DEBUG,
628			"fail to find key:%s. delete failed\n", p_key);
629		res = 1;
630	}
631	cl_spinlock_release(&p_domain_imp->lock);
632
633	OSM_LOG_EXIT(p_log);
634	return res;
635}
636
637#ifdef TEST_OSMDB
638#include <stdlib.h>
639#include <math.h>
640
641int main(int argc, char **argv)
642{
643	osm_db_t db;
644	osm_log_t log;
645	osm_db_domain_t *p_dbd;
646	cl_list_t keys;
647	cl_list_iterator_t kI;
648	char *p_key;
649	char *p_val;
650	int i;
651
652	cl_list_construct(&keys);
653	cl_list_init(&keys, 10);
654
655	osm_log_init_v2(&log, TRUE, 0xff, "/var/log/osm_db_test.log", 0, FALSE);
656
657	osm_db_construct(&db);
658	if (osm_db_init(&db, &log)) {
659		printf("db init failed\n");
660		exit(1);
661	}
662
663	p_dbd = osm_db_domain_init(&db, "lid_by_guid");
664
665	if (osm_db_restore(p_dbd)) {
666		printf("failed to restore\n");
667	}
668
669	if (osm_db_keys(p_dbd, &keys)) {
670		printf("failed to get keys\n");
671	} else {
672		kI = cl_list_head(&keys);
673		while (kI != cl_list_end(&keys)) {
674			p_key = cl_list_obj(kI);
675			kI = cl_list_next(kI);
676
677			p_val = osm_db_lookup(p_dbd, p_key);
678			printf("key = %s val = %s\n", p_key, p_val);
679		}
680	}
681
682	cl_list_remove_all(&keys);
683
684	/* randomly add and remove numbers */
685	for (i = 0; i < 10; i++) {
686		int k;
687		float v;
688		int is_add;
689		char val_buf[16];
690		char key_buf[16];
691
692		k = floor(1.0 * rand() / RAND_MAX * 100);
693		v = rand();
694		sprintf(key_buf, "%u", k);
695		sprintf(val_buf, "%u", v);
696
697		is_add = (rand() < RAND_MAX / 2);
698
699		if (is_add) {
700			osm_db_update(p_dbd, key_buf, val_buf);
701		} else {
702			osm_db_delete(p_dbd, key_buf);
703		}
704	}
705	if (osm_db_keys(p_dbd, &keys)) {
706		printf("failed to get keys\n");
707	} else {
708		kI = cl_list_head(&keys);
709		while (kI != cl_list_end(&keys)) {
710			p_key = cl_list_obj(kI);
711			kI = cl_list_next(kI);
712
713			p_val = osm_db_lookup(p_dbd, p_key);
714			printf("key = %s val = %s\n", p_key, p_val);
715		}
716	}
717	if (osm_db_store(p_dbd))
718		printf("failed to store\n");
719
720	osm_db_destroy(&db);
721	cl_list_destroy(&keys);
722}
723#endif
724